package com.interpreter.part3;

import com.interpreter.common.CommonUtil;
import com.interpreter.common.Constant;
import com.interpreter.common.Token;

import java.math.BigDecimal;


public class Interpreter {

  public String text;
  public int pos;
  public Token currentToken;
  /** 增加一个字段, 用来存储当前正在处理的字符 */
  public Character currentChar;

  public Interpreter(String text) {
    this.text = text;
    this.pos = 0;
    this.currentToken = null;
    this.currentChar = null;
    this.assignCurrentCharAndMovePos();
  }

  public void error() {
    throw new RuntimeException("解析给定文本出现错误,请检查");
  }

  /** 获取当前游标指向的那个字符,并返回, 同时将游标向前移动一位 */
  public void assignCurrentCharAndMovePos() {
    if (this.pos > this.text.length() - 1) { this.currentChar = null; return; }
    this.currentChar = this.text.charAt(this.pos);
    this.pos++;
  }

  /** 不断的检查 "当前字符" 是不是空格, 如果是, 则不断的向后移动游标 */
  public void skipAnnoyingSpace() {
    while (this.currentChar != null && CommonUtil.isSpace(this.currentChar)) {
      this.assignCurrentCharAndMovePos();
    }
  }

  /**
   * 查看当前字符, 如果字符是数字, 则存储到 "数字字符桶" 中, 直到遇见非数字的字符
   * 最后将字符串形式的数字转换为 BigDecimal 返回,
   * 为什么是 BigDecimal? 因为我们无法预料用户输入的整数到底多大, 很有可能溢出(int/log)
   * 当前阶段不需要考虑太多, 直接采用 BigDecimal 比较方便
   */
  public BigDecimal fetchIntegerNumber() {
    // StringBuilder 就是我们图上画出的 "数字字符桶"
    StringBuilder sb = new StringBuilder();
    while (this.currentChar != null && CommonUtil.isInteger(this.currentChar)) {
      sb.append(this.currentChar);
      this.assignCurrentCharAndMovePos();
    }
    return new BigDecimal(sb.toString());
  }

  /** 遍历字符数组, 然后对每个字符进行判断, 将其包装为 Token 之后进行返回 */
  public Token getNextToken() {
    while (this.currentChar != null) {
      this.skipAnnoyingSpace();
      // 如果 char 为数字, 则包装为 INTEGER token, 此 token 可能会包含 1~N 个字符
      if (CommonUtil.isInteger(this.currentChar)) {
        return new Token(Constant.TOKEN_INTEGER, this.fetchIntegerNumber());
      }
      // 如果 char 为 + 符号, 则包装为 PLUS token
      if (this.currentChar == '+') {
        this.assignCurrentCharAndMovePos();
        return new Token(Constant.TOKEN_PLUS, '+');
      }
      // 如果 char 为 - 符号, 则包装为 MINUS token
      if (this.currentChar == '-') {
        this.assignCurrentCharAndMovePos();
        return new Token(Constant.TOKEN_MINUS, '-');
      }
      // 以上都不是, 则证明给定的文本中包含了非法字符, 我们抛出错误, 拒绝执行
      this.error();
    }
    // 循环退出, this.currentChar 为 null, 没有下一个字符了, 返回 eof
    return Token.EofToken();
  }

  /**
   * 检查当前 token 的类型是不是我们想要的, 如果是我们想要的, 则返回下一个 token
   * 如果不是我们预计的 token 类型, 则报错
   * 此处可以看做最简单的语法检查
   */
  public void checkAndMoveToNext(String tokenType) {
    if (this.currentToken.tokenType.equals(tokenType)) {
      this.currentToken = this.getNextToken();
    } else {
      this.error();
    }
  }

  public BigDecimal factor() {
    Token token = this.currentToken;
    this.checkAndMoveToNext(Constant.TOKEN_INTEGER);
    return (BigDecimal) token.value;
  }

  public Object expression() {
    this.currentToken = this.getNextToken();
    BigDecimal result = this.factor();

    while (this.currentToken != null &&
        (this.currentToken.tokenType.equals(Constant.TOKEN_PLUS) ||
        this.currentToken.tokenType.equals(Constant.TOKEN_MINUS))) {
      Token token = this.currentToken;
      if (token.tokenType.equals(Constant.TOKEN_PLUS)) {
        this.checkAndMoveToNext(Constant.TOKEN_PLUS);
        result = result.add(this.factor());
      } else if (token.tokenType.equals(Constant.TOKEN_MINUS)) {
        this.checkAndMoveToNext(Constant.TOKEN_MINUS);
        result = result.subtract(this.factor());
      }
    }
    return result;
  }

  public static void main(String[] args) {
    String text = "90 -  1 +1";
    Interpreter interpreter = new Interpreter(text);
    Object result = interpreter.expression();
    System.out.println(result);
  }
}


